iT邦幫忙

DAY 3
10

node.js伺服器實戰系列 第 3

node.js伺服器實戰(3) - 基礎知識

  • 分享至 

  • xImage
  •  

要開發node.js程式,還是需要知道一些基本知識,才容易上手。
event loop
Javascript執行的life cycle,大致可以分成兩個部份:

  1. 執行global scope中的程式碼,函數除非在這裡被執行,否則只有函數的定義與指向函數的參考
  2. 等待事件發生,執行事件的處理函數(包含setTimeout、setInterval等定期觸發的函數)

通常global scope的程式只會執行一次(不過在瀏覽器中,可以利用動態新增script tag來進入載入的javascript的global scope),執行完畢後,就進入event loop,執行所有被觸發的事件處理函數。事件處理函數被觸發以後,會排進一個queue結構,透過一個loop,從queue裡面取出被觸發的函數來執行。

不論在瀏覽器或是在伺服器端執行Javascript,運行的機制都是這樣。所以雖然可以非同步執行,但是其實同時只會有一個函數在執行,而且一個函數執行完畢之前,其他函數都不會執行。這樣的結構有一些好處,除了避開像Thread這樣平行處理的複雜度(同時不會有兩個函數執行,所以共用的變數不需要鎖定),也減少了建立Thread所需要得額外成本,所以反應速度很快。

但是Javascript使用的event loop結構也有一些缺點,首先,只要有一個函數執行時間比較久,就會影響系統的反應速度。另外,這樣的結構基本上只能在單一執行緒中執行,在目前CPU幾乎都是多核心的環境下,很難發揮最好的效能。

目前要解決效能問題的方式,通常都是依照系統可使用的核心數,來同時執行幾個伺服器程式,然後在前方利用Reverse Proxy來做負載均衡,把負載分散到各個獨立執行的伺服器程式。不過最近看到Intel有計劃在發展有多核心執行能力的Javascript引擎,也許過不久也會發展出可以「無痛」利用所有核心的node.js伺服器。(https://github.com/RiverTrail/RiverTrail

commonjs模組標準
參考:http://www.commonjs.org/specs/modules/1.0/

簡單地說,在模組中,你可以透過exports變數把做好的API輸出到用require(identifier)來引入指定模組的程式來使用。用簡單的程式來看會更清楚:

模組程式mod.js:

 exports.add = function(a, b) {return a+b;};

那要在程式中使用模組,可以這樣做:

 var add = require(mod).add;
 console.log(add(1,2));
 //顯示3

在node.js中,除了使用exports,還可以利用module.exports來直接輸出API。例如上面的範例可以改成:

 module.exports = function(a, b) {return a+b;};

使用起來更簡單:

 var add = require(mod);
 console.log(add(1,2));//結果一樣

另外要補充一點,除了昨天在環境配置中提到的使用NODE_PATH來指定預設的模組路徑,在應用程式所在位置建立一個叫做node_modules的子目錄,也有同樣的效果。node會在這些目錄中尋找模組,這樣就可以省去搜尋模組檔案的麻煩。

另外,如果使用到多個檔案,通常需要用目錄而不是單一檔案來放置模組。這時候可以用目錄名稱當作identtifier來載入模組,但是需要符合幾個規則:

  1. 模組主檔叫做index.js
  2. 在模組目錄中的package.json檔案中,定義好模組名稱、主檔等資訊

node會使用一定的規則來搜尋、載入要使用的模組。詳細說明可以參考:http://nodejs.org/docs/v0.5.9/api/modules.html

node.js的Global環境
Javascript除了在ECMA-262中定義的核心物件外,所能使用的函數及物件,都是透過global環境取得的。(當然,有了模組機制後,就不需要這麼麻煩)所以先了解一下到底node.js的global環境提供了什麼,會很有幫助。

node.js主要是伺服器端的執行環境,比較不必擔心一些在瀏覽器環境中需要考慮的安全問題。所以在node.js中使用for...in敘述來做reflection,通常不會有太大問題。像這樣就可以輕鬆知道global環境有什麼可以使用的物件及函數:

 (function(obj){for(var i in obj){console.log(i+': '+ typeof obj[i]);})(this);

在node的console跑跑看(執行node時不加參數):

 > (function(obj){for(var i in obj){console.log(i + ': ' + typeof obj[i]);}})(this);
 root: object
 require: function
 Float64Array: function
 Uint32Array: function
 Uint16Array: function
 clearTimeout: function
 Int32Array: function
 global: object
 ArrayBuffer: function
 Int16Array: function
 DataView: function
 Float32Array: function
 Int8Array: function
 setInterval: function
 console: object
 clearInterval: function
 GLOBAL: object
 module: object
 Buffer: function
 Uint8Array: function
 setTimeout: function
 process: object

不過如果跟API文件中的說明對照一下(http://nodejs.org/docs/v0.5.9/api/globals.html ),還是缺了幾個東西:

 __filename
 __dirname
 exports

簡單說明一下:
* root, global, GLOBAL都是global物件的別名,就像在瀏覽器環境中的window物件一樣。
* ArrayBuffer, Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array, DataView等,都是最近才加到開發版中的TypedArray相關物件
* process就是Process物件,用來提供一些工具程式以及行程資訊
* Buffer物件,是node.js用來處理binary資料的物件
* console是node.js用來處理stdio的物件
* setTimeout, clearTimeout, setInterval, clearInterval,與瀏覽器中的同名函數使用方式差不多
* __filename,就是執行的js程式檔名,如果是在模組裡面,就是模組檔案的檔名,而不是主程式的檔名
* __dirname,同上,不過是js程式檔目錄的目錄名

API本身的篇幅較長,還是放到明天再來介紹,順便寫一個簡單偵測API的程式。

相關文章


上一篇
node.js伺服器實戰(2) - 環境建置
下一篇
node.js伺服器實戰(4) - 內建模組與http伺服器開發的必備知識
系列文
node.js伺服器實戰33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
0
1
海綿寶寶
iT邦大神 1 級 ‧ 2011-10-13 10:28:26

寫的這麼專業又這麼好
那我們這些哈拉組的該怎麼辦?

不想理你,意思意思推個文就算了
醉

1
1
CaesarChi
iT邦新手 3 級 ‧ 2011-10-16 09:14:14

這篇的內容,真的很詳盡又專業!
感謝大公分享!

我要留言

立即登入留言